package main

import (
	fm "fmt" // 给 fmt 包起一个别名 fm
	"go/types"
)

/**
变量介绍
*/
var overall int // 全局变量，就算没被用到，也不会报错，这里仅作演示

func main() {

	/*
		一. 未初始化变量 声明的标准格式： var 变量名 变量类型

		TIPS：如果代码中有未使用的变量（不管是否初始化），那么代码编译的时候就会报"a declared and not used"错误，当然如果使用 IDE 也会及时提醒的。因为Go要求在代码中所有声明的变量都需要被用到，但是全局变量除外。例如前面的变量 overall。
	*/
	var a int
	var aah float32
	var aaa string
	var flag bool
	var sl types.Slice
	var ptr *int
	/*
		未初始化变量的默认值有如下特点:

			• 整型和浮点型变量默认值：0
			• 字符串默认值为空字符串
			• 布尔型默认值为false
			• 函数、指针变量、切片默认值为nil
	*/
	fm.Println(a)
	fm.Println(aah)
	fm.Println(aaa)
	fm.Println(flag)
	fm.Println(sl)
	fm.Println(len(aaa))
	fm.Println(ptr)

	/**
	二. 初始化变量 声明的标准格式： var 变量名 变量类型 = 表达式
	*/
	var d int = 10
	fm.Println(d)

	/**
	三. 初始化变量自动推断类型 声明的标准格式： var 变量名 = 表达式
	*/
	var e = 10
	fm.Println(e)

	/**
	四. 初始化变量 声明的简短声明格式（短变量声明格式）: 变量名 := 表达式

	TIPS：声明变量的首选形式，但是它【只能被用在函数体内，不能用于全局变量的声明与赋值】
	*/
	f := 10
	fm.Println(f)

	/*
		五. 变量多重赋值，是指多个变量同时赋值。

		TIPS：多重赋值时，左值和右值按照从左到右的顺序赋值。这种方法在错误处理和函数当中会大量使用
		这里以简单的算法交换变量为例，避免了传统写法中新定义中间变量需要消耗内存的不足
	*/
	var left int = 20
	var right int = 30
	fm.Println(left)
	fm.Println(right)
	right, left = left, right
	fm.Println(left)
	fm.Println(right)

	/*
		六. 匿名变量，用下画线“_”表示。“_”是特殊标识符，用来忽略结果

		TIPS：匿名变量既不占用命名空间，也不会分配内存。所以对应未使用的变量就可以使用匿名变量来避免编译时报"a declared and not used" 的错误
	*/
	var _ int

	/*
			关于“_”是特殊标识符介绍：

				1. 下划线用在import中。当导入一个包时，该包下的文件里所有init()函数都会被执行。然而有些时候我们并不需要把整个包都导入进来，仅仅是是
		           希望它执行init()函数而已。这个时候就可以使用【import _ 包路径】引用该包，仅仅是为了调用init()函数，无法通过包名来调用包中的其
				   他函数。例如：_ "github.com/go-sql-driver/mysql"，可见 14database/1/Mysql1.go；
				2. 下划线用在代码中。用于忽略变量，比如os.Open，返回值为*os.File，error，常规写法是 f,err := os.Open("xxxxxxx")，如果此时不
				   需要关心返回的错误值，就可以用f, _ := os.Open("xxxxxx")，这样就忽略了error变量；
				3. 下划线在代码中之占位符。意思是那个位置本应赋给某个值，但是咱们不需要这个值。所以就把该值赋给下划线，意思是丢掉不要。这样编译器可以
		           更好的优化，任何类型的单个值都可以丢给下划线。这种情况是占位用的，方法返回两个结果，而你只想要一个结果。那另一个就用 "_" 占位，变量
				   如果不使用编译器是会报错的
	*/

	// 舍弃函数的第一个返回值
	_, h := GetData()
	// 舍弃函数的第二个返回值
	g, _ := GetData()
	fm.Println(g)
	fm.Println(h)

	/*
		七. 批量声明变量
	*/
	var (
		aa string
		bb int
		cc bool
		dd float32
	)
	fm.Println(aa)
	fm.Println(bb)
	fm.Println(cc)
	fm.Println(dd)

	/*
		八. 一次赋值多个（类型不要求一定相同，这和 java 中的声明要求是不同的）
	*/
	gg, oo, kk := 1, false, "string"
	fm.Println(gg, oo, kk)
	var ll, nn = 1, "sddd"
	fm.Println(ll, true, nn)
}

/*
	匿名变量使用之定义一个函数功能为：返回两个int型变量，第一个返回10，第二个返回20，第一次调用舍弃第二个返回值，第二次调用舍弃第一个返回值
*/
func GetData() (int, int) {

	return 100, 200
}
